Practical Memory Move Routines by Bruce Clark
[Up to Source Code Repository]
Here are some reasonably fast general-purpose routines for moving blocks of memory. You simply specify the address to move from, the address to move to, and the size of the block. When SIZE is zero, no bytes are moved. SIZEL and SIZEH do not need to be consecutive memory locations, or even on the zero page for that matter. These routines only take one additional cycle if SIZEL is not on the zero page. Likewise, they only take one additional cycle if SIZEH is not on the zero page. Note that this adds only to the total number of cycles, not to the number of cycles per byte, since neither SIZEL nor SIZEH is inside a loop anywhere.
These routines are intended to be both flexible and practical, without being excessively lengthy or excessively slow. To that end, they can be placed in ROM or in RAM.
There are three routines moving memory upward (i.e. to a higher address), each of which is tailored to a slightly different set of input parameters.
; Move memory down ; ; FROM = source start address ; TO = destination start address ; SIZE = number of bytes to move ; MOVEDOWN LDY #0 LDX SIZEH BEQ MD2 MD1 LDA (FROM),Y ; move a page at a time STA (TO),Y INY BNE MD1 INC FROM+1 INC TO+1 DEX BNE MD1 MD2 LDX SIZEL BEQ MD4 MD3 LDA (FROM),Y ; move the remaining bytes STA (TO),Y INY DEX BNE MD3 MD4 RTS ; Move memory up ; ; FROM = source start address ; TO = destination start address ; SIZE = number of bytes to move ; MOVEUP LDX SIZEH ; the last byte must be moved first CLC ; start at the final pages of FROM and TO TXA ADC FROM+1 STA FROM+1 CLC TXA ADC TO+1 STA TO+1 INX ; allows the use of BNE after the DEX below LDY SIZEL BEQ MU3 DEY ; move bytes on the last page first BEQ MU2 MU1 LDA (FROM),Y STA (TO),Y DEY BNE MU1 MU2 LDA (FROM),Y ; handle Y = 0 separately STA (TO),Y MU3 DEY DEC FROM+1 ; move the next page (if any) DEC TO+1 DEX BNE MU1 RTS ; Move memory up ; ; FROM = 1 + source end address ; TO = 1 + destination end address ; SIZE = number of bytes to move ; MOVEUP LDY #$FF LDX SIZEH BEQ MU3 MU1 DEC FROM+1 DEC TO+1 MU2 LDA (FROM),Y ; move a page at a time STA (TO),Y DEY BNE MU2 LDA (FROM),Y ; handle Y = 0 separately STA (TO),Y DEY DEX BNE MU1 MU3 LDX SIZEL BEQ MU5 DEC FROM+1 DEC TO+1 MU4 LDA (FROM),Y ; move the remaining bytes STA (TO),Y DEY DEX BNE MU4 MU5 RTS ; Move memory up ; ; FROM = source end address ; TO = destination end address ; SIZE = number of bytes to move ; MOVEUP LDY #0 LDX SIZEH BEQ MU3 MU1 LDA (FROM),Y ; handle Y = 0 separately STA (TO),Y DEY DEC FROM+1 DEC TO+1 MU2 LDA (FROM),Y ; move a page at a time STA (TO),Y DEY BNE MU2 DEX BNE MU1 MU3 LDX SIZEL BEQ MU5 LDA (FROM),Y ; handle Y = 0 separately STA (TO),Y DEY DEX BEQ MU5 DEC FROM+1 DEC TO+1 MU4 LDA (FROM),Y ; move the remaining bytes STA (TO),Y DEY DEX BNE MU4 MU5 RTSEven more speed can be gained by using self-modifying code, i.e. replacing the (ZeroPage),Y addressing mode with the Absolute,Y addressing mode. This will take 2 fewer cycles per byte. There will be some additional cycles from the added instructions that self-modify the code, but the self-modification occurs only once, and therefore adds these cycles to total number of cycles, rather than the number of cycles per byte moved. As always, the instructions that are self-modified can't be located in ROM.
Last page update: April 3, 2004.